/*     */ package com.zimbra.cs.fb;
/*     */ 
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.soap.Element;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Server;
/*     */ import com.zimbra.cs.mailbox.MailItem.Type;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.MailboxListener;
/*     */ import com.zimbra.cs.mailbox.MailboxListener.ChangeNotification;
/*     */ import com.zimbra.cs.mailbox.MailboxManager;
/*     */ import com.zimbra.cs.service.mail.ToXML;
/*     */ import com.zimbra.cs.session.PendingModifications;
/*     */ import java.io.File;
/*     */ import java.io.FileDescriptor;
/*     */ import java.io.FileInputStream;
/*     */ import java.io.FileOutputStream;
/*     */ import java.io.IOException;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Calendar;
/*     */ import java.util.Collections;
/*     */ import java.util.EnumSet;
/*     */ import java.util.GregorianCalendar;
/*     */ import java.util.HashMap;
/*     */ import java.util.HashSet;
/*     */ import java.util.LinkedList;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public abstract class FreeBusyProvider
/*     */ {
/*     */   public static class Listener
/*     */     extends MailboxListener
/*     */   {
/*     */     public void notify(MailboxListener.ChangeNotification notification)
/*     */     {
/*  57 */       FreeBusyProvider.mailboxChanged(notification.mailboxAccount.getId(), notification.mods.changedTypes);
/*     */     }
/*     */     
/*  60 */     private static final Set<MailItem.Type> TYPES = EnumSet.of(MailItem.Type.APPOINTMENT);
/*     */     
/*     */ 
/*     */ 
/*  64 */     public Set<MailItem.Type> registerForItemTypes() { return TYPES; }
/*     */   }
/*     */   
/*     */   public static class Request { Account requestor;
/*     */     String email;
/*     */     long start;
/*     */     
/*  71 */     public Request(Account req, String em, long s, long e, int f) { this(req, em, s, e, f, -1); }
/*     */     
/*     */     public Request(Account req, String em, long s, long e, int f, int hops)
/*     */     {
/*  75 */       this.requestor = req;this.email = em;
/*  76 */       Calendar cal = GregorianCalendar.getInstance();
/*  77 */       cal.setTimeInMillis(s);
/*  78 */       cal.set(13, 0);
/*  79 */       this.start = cal.getTimeInMillis();
/*  80 */       cal.setTimeInMillis(e);
/*  81 */       cal.set(13, 0);
/*  82 */       this.end = cal.getTimeInMillis();
/*  83 */       this.folder = f;
/*  84 */       this.hopcount = hops;
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */     public Account getRequestor()
/*     */     {
/*  95 */       return this.requestor;
/*     */     }
/*     */     
/*     */     public String getEmail() {
/*  99 */       return this.email;
/*     */     }
/*     */     
/*     */     public long getStart() {
/* 103 */       return this.start;
/*     */     }
/*     */     
/*     */     public long getEnd() {
/* 107 */       return this.end;
/*     */     }
/*     */     
/*     */     public int getFolder() {
/* 111 */       return this.folder;
/*     */     }
/*     */     
/*     */     public Object getData() {
/* 115 */       return this.data;
/*     */     }
/*     */     
/*     */     public void setData(Object obj) {
/* 119 */       this.data = obj;
/*     */     }
/*     */     
/*     */     public int getHopcount() {
/* 123 */       return this.hopcount;
/*     */     }
/*     */     
/*     */     public void incrementHopcount() {
/* 127 */       if (this.hopcount < 0) {
/* 128 */         this.hopcount = 0;
/*     */       } else
/* 130 */         this.hopcount += 1;
/*     */     }
/*     */     
/*     */     public static long offsetInterval(long time, int intervalInMins) {
/* 134 */       Calendar cal = GregorianCalendar.getInstance();
/* 135 */       cal.setTimeInMillis(time);
/* 136 */       int min = cal.get(12);
/* 137 */       int off = min % intervalInMins;
/* 138 */       cal.set(12, min - off);
/* 139 */       return cal.getTimeInMillis();
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */     long end;
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */     int folder;
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */     Object data;
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */     int hopcount;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public static void register(FreeBusyProvider p)
/*     */   {
/* 169 */     synchronized (sPROVIDERS) {
/* 170 */       sPROVIDERS.add(p);
/*     */     }
/*     */   }
/*     */   
/*     */   private static FreeBusySyncQueue startConsumerThread(FreeBusyProvider p) {
/* 175 */     String name = p.getName();
/* 176 */     FreeBusySyncQueue queue = (FreeBusySyncQueue)sPUSHQUEUES.get(name);
/* 177 */     if (queue != null) {
/* 178 */       ZimbraLog.fb.warn("free/busy provider " + name + " has been already registered.");
/*     */     }
/* 180 */     queue = new FreeBusySyncQueue(p);
/* 181 */     sPUSHQUEUES.put(name, queue);
/* 182 */     new Thread(queue).start();
/* 183 */     return queue;
/*     */   }
/*     */   
/*     */   public static void mailboxChanged(String accountId) {
/* 187 */     mailboxChanged(accountId, EnumSet.of(MailItem.Type.APPOINTMENT));
/*     */   }
/*     */   
/*     */   public static void mailboxChanged(String accountId, Set<MailItem.Type> changedType) {
/* 191 */     for (FreeBusyProvider prov : sPROVIDERS)
/* 192 */       if ((prov.registerForMailboxChanges(accountId)) && (!Collections.disjoint(changedType, prov.registerForItemTypes()))) {
/* 193 */         FreeBusySyncQueue queue = (FreeBusySyncQueue)sPUSHQUEUES.get(prov.getName());
/* 194 */         if (queue == null)
/* 195 */           queue = startConsumerThread(prov);
/* 196 */         synchronized (queue) {
/* 197 */           if (!queue.contains(accountId))
/*     */           {
/* 199 */             queue.addLast(accountId);
/*     */             try {
/* 201 */               queue.writeToDisk();
/*     */             } catch (IOException e) {
/* 203 */               ZimbraLog.fb.error("can't write to the queue " + queue.getFilename());
/*     */             }
/* 205 */             queue.notify();
/*     */           }
/*     */         }
/*     */       }
/*     */   }
/*     */   
/* 211 */   public void addResults(Element response) { for (FreeBusy fb : getResults())
/* 212 */       ToXML.encodeFreeBusy(response, fb);
/*     */   }
/*     */   
/*     */   public static List<FreeBusy> getRemoteFreeBusy(Account requestor, List<String> remoteIds, long start, long end, int folder, int hopcount) {
/* 216 */     Set<FreeBusyProvider> providers = getProviders();
/* 217 */     ArrayList<FreeBusy> ret = new ArrayList();
/* 218 */     for (String emailAddr : remoteIds) {
/* 219 */       Request req = new Request(requestor, emailAddr, start, end, folder, hopcount);
/* 220 */       boolean succeed = false;
/* 221 */       for (FreeBusyProvider prov : providers) {
/*     */         try {
/* 223 */           prov.addFreeBusyRequest(req);
/* 224 */           succeed = true;
/*     */         }
/*     */         catch (FreeBusyUserNotFoundException e) {}
/*     */       }
/* 228 */       if (!succeed) {
/* 229 */         ZimbraLog.fb.error("can't find free/busy provider for user " + emailAddr);
/* 230 */         ret.add(FreeBusy.nodataFreeBusy(emailAddr, start, end));
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*     */ 
/* 236 */     Map<String, ArrayList<FreeBusy>> freebusyMap = new HashMap();
/* 237 */     for (FreeBusyProvider prov : providers) {
/* 238 */       for (FreeBusy fb : prov.getResults()) {
/* 239 */         ArrayList<FreeBusy> freebusyList = (ArrayList)freebusyMap.get(fb.getName());
/* 240 */         if (freebusyList == null) {
/* 241 */           freebusyList = new ArrayList();
/* 242 */           freebusyMap.put(fb.getName(), freebusyList);
/*     */         }
/* 244 */         freebusyList.add(fb);
/*     */       }
/*     */     }
/*     */     
/* 248 */     for (Map.Entry<String, ArrayList<FreeBusy>> entry : freebusyMap.entrySet()) {
/* 249 */       ArrayList<FreeBusy> freebusyList = (ArrayList)entry.getValue();
/* 250 */       FreeBusy freebusy = null;
/* 251 */       for (FreeBusy fb : freebusyList) {
/* 252 */         if (freebusy == null) {
/* 253 */           freebusy = fb;
/*     */ 
/*     */         }
/* 256 */         else if (!fb.getBusiest().equals("F")) {
/* 257 */           freebusy = fb;
/*     */         }
/*     */       }
/* 260 */       ret.add(freebusy);
/*     */     }
/* 262 */     return ret;
/*     */   }
/*     */   
/*     */   public static void getRemoteFreeBusy(Account requestor, Element response, List<String> remoteIds, long start, long end, int folder, int hopcount) {
/* 266 */     for (FreeBusy fb : getRemoteFreeBusy(requestor, remoteIds, start, end, folder, hopcount)) {
/* 267 */       ToXML.encodeFreeBusy(response, fb);
/*     */     }
/*     */   }
/*     */   
/*     */   protected FreeBusy getFreeBusy(String accountId, int folderId) throws ServiceException {
/* 272 */     Account account = Provisioning.getInstance().getAccountById(accountId);
/* 273 */     if ((account == null) || (!Provisioning.onLocalServer(account)))
/* 274 */       return null;
/* 275 */     Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(accountId);
/* 276 */     if (mbox == null)
/* 277 */       return null;
/* 278 */     return mbox.getFreeBusy(null, cachedFreeBusyStartTime(accountId), cachedFreeBusyEndTime(accountId), folderId);
/*     */   }
/*     */   
/*     */   protected String getEmailAddress(String accountId) {
/* 282 */     Account acct = null;
/*     */     try {
/* 284 */       acct = Provisioning.getInstance().get(Key.AccountBy.id, accountId);
/*     */     }
/*     */     catch (ServiceException e) {}
/* 287 */     if (acct == null)
/* 288 */       return null;
/* 289 */     return acct.getName();
/*     */   }
/*     */   
/*     */   protected List<FreeBusy> getEmptyList(ArrayList<Request> req) {
/* 293 */     ArrayList<FreeBusy> ret = new ArrayList();
/* 294 */     for (Request r : req)
/* 295 */       ret.add(FreeBusy.nodataFreeBusy(r.email, r.start, r.end));
/* 296 */     return ret;
/*     */   }
/*     */   
/*     */ 
/* 300 */   public FreeBusySyncQueue getSyncQueue() { return (FreeBusySyncQueue)sPUSHQUEUES.get(getName()); }
/*     */   
/*     */   public static FreeBusyProvider getProvider(String name) {
/* 303 */     for (FreeBusyProvider p : sPROVIDERS)
/* 304 */       if (p.getName().equals(name))
/* 305 */         return p;
/* 306 */     return null;
/*     */   }
/*     */   
/* 309 */   public static Set<FreeBusyProvider> getProviders() { HashSet<FreeBusyProvider> ret = new HashSet();
/* 310 */     for (FreeBusyProvider p : sPROVIDERS)
/* 311 */       ret.add(p.getInstance());
/* 312 */     return ret;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/* 318 */   private static HashSet<FreeBusyProvider> sPROVIDERS = new HashSet();
/* 319 */   private static HashMap<String, FreeBusySyncQueue> sPUSHQUEUES = new HashMap();
/* 320 */   static { new ExchangeFreeBusyProvider();
/* 321 */     new ExchangeEWSFreeBusyProvider();
/*     */   }
/*     */   
/*     */ 
/* 325 */   public String getQueueFilename() { return LC.freebusy_queue_directory.value() + "queue-" + getName(); }
/*     */   
/*     */   public abstract FreeBusyProvider getInstance();
/*     */   
/*     */   public static class FreeBusyUserNotFoundException extends Exception
/*     */   {}
/*     */   
/* 332 */   public static class FreeBusySyncQueue extends LinkedList<String> implements Runnable { FreeBusySyncQueue(FreeBusyProvider prov) { this.mProvider = prov;
/* 333 */       this.mLastFailed = 0L;
/* 334 */       this.mShutdown = false;
/* 335 */       this.mFilename = prov.getQueueFilename();
/* 336 */       File f = new File(this.mFilename);
/* 337 */       if (!f.exists()) {
/* 338 */         f.getParentFile().mkdirs();
/*     */       }
/*     */       try {
/* 341 */         readFromDisk();
/*     */       } catch (IOException e) {
/* 343 */         ZimbraLog.fb.error("error reading from the queue", e);
/*     */       }
/*     */     }
/*     */     
/*     */     private boolean mShutdown;
/*     */     public void run() {
/* 349 */       Thread.currentThread().setName(this.mProvider.getName() + " Free/Busy Sync Queue");
/* 350 */       while (!this.mShutdown)
/*     */         try {
/* 352 */           String acctId = null;
/* 353 */           synchronized (this) {
/* 354 */             if (size() > 0)
/*     */             {
/*     */ 
/* 357 */               long now = System.currentTimeMillis();
/* 358 */               long retryInterval = 60000L;
/*     */               try {
/* 360 */                 retryInterval = Provisioning.getInstance().getLocalServer().getFreebusyPropagationRetryInterval();
/*     */               }
/*     */               catch (Exception e) {}
/* 363 */               if (now < this.mLastFailed + retryInterval) {
/* 364 */                 wait(retryInterval);
/* 365 */                 continue;
/*     */               }
/* 367 */               acctId = (String)getFirst();
/*     */             } else {
/* 369 */               wait();
/*     */             }
/*     */           }
/* 372 */           if (acctId != null)
/*     */           {
/*     */ 
/* 375 */             boolean success = this.mProvider.handleMailboxChange(acctId);
/*     */             
/* 377 */             synchronized (this) {
/* 378 */               removeFirst();
/*     */             }
/* 380 */             if (!success) {
/* 381 */               synchronized (this) {
/* 382 */                 addLast(acctId);
/*     */               }
/* 384 */               this.mLastFailed = System.currentTimeMillis();
/*     */             }
/* 386 */             writeToDisk();
/*     */           }
/*     */         } catch (Exception e) {
/* 389 */           this.mLastFailed = System.currentTimeMillis();
/* 390 */           ZimbraLog.fb.error("error while syncing freebusy for " + this.mProvider.getName(), e);
/*     */         }
/*     */     }
/*     */     
/*     */     public void shutdown() {
/* 395 */       this.mShutdown = true;
/*     */     }
/*     */     
/*     */     private long mLastFailed;
/*     */     private static final int DEFAULT_RETRY_INTERVAL = 60000;
/*     */     private static final int MAX_FILE_SIZE = 1024000;
/*     */     private String mFilename;
/*     */     private FreeBusyProvider mProvider;
/*     */     public synchronized void writeToDisk()
/*     */       throws IOException
/*     */     {
/* 406 */       StringBuilder buf = new StringBuilder(Integer.toString(size() + 1));
/* 407 */       for (String id : this)
/* 408 */         buf.append("\n").append(id);
/* 409 */       if (buf.length() > 1024000) {
/* 410 */         ZimbraLog.fb.error("The free/busy replication queue is too large. #elem=" + size());
/* 411 */         return;
/*     */       }
/* 413 */       FileOutputStream out = null;
/*     */       try {
/* 415 */         out = new FileOutputStream(this.mFilename);
/* 416 */         out.write(buf.toString().getBytes());
/* 417 */         out.getFD().sync();
/*     */       } finally {
/* 419 */         if (out != null)
/* 420 */           out.close();
/*     */       }
/*     */     }
/*     */     
/*     */     public synchronized void readFromDisk() throws IOException {
/* 425 */       File f = new File(this.mFilename);
/* 426 */       if (!f.exists())
/* 427 */         f.createNewFile();
/* 428 */       long len = f.length();
/* 429 */       if (len > 1024000L) {
/* 430 */         ZimbraLog.fb.error("The free/busy replication queue is too large: " + this.mFilename + " (" + len + ")");
/* 431 */         return;
/*     */       }
/* 433 */       FileInputStream in = null;
/* 434 */       String[] tokens = null;
/*     */       try {
/* 436 */         in = new FileInputStream(f);
/* 437 */         byte[] buf = ByteUtil.readInput(in, (int)len, 1024000);
/* 438 */         tokens = new String(buf, "UTF-8").split("\n");
/*     */       } finally {
/* 440 */         if (in != null)
/* 441 */           in.close();
/*     */       }
/* 443 */       if (tokens.length < 2)
/* 444 */         return;
/* 445 */       int numTokens = Integer.parseInt(tokens[0]);
/* 446 */       if (numTokens != tokens.length) {
/* 447 */         ZimbraLog.fb.error("The free/busy replication queue is inconsistent: numTokens=" + numTokens + ", actual=" + tokens.length);
/*     */         
/* 449 */         return;
/*     */       }
/* 451 */       clear();
/* 452 */       Collections.addAll(this, tokens);
/* 453 */       removeFirst();
/*     */     }
/*     */     
/*     */     public String getFilename() {
/* 457 */       return this.mFilename;
/*     */     }
/*     */   }
/*     */   
/*     */   public abstract String getName();
/*     */   
/*     */   public abstract void addFreeBusyRequest(Request paramRequest)
/*     */     throws FreeBusyProvider.FreeBusyUserNotFoundException;
/*     */   
/*     */   public abstract List<FreeBusy> getResults();
/*     */   
/*     */   public abstract boolean registerForMailboxChanges();
/*     */   
/*     */   public abstract boolean registerForMailboxChanges(String paramString);
/*     */   
/*     */   public abstract Set<MailItem.Type> registerForItemTypes();
/*     */   
/*     */   public abstract boolean handleMailboxChange(String paramString);
/*     */   
/*     */   public abstract long cachedFreeBusyStartTime();
/*     */   
/*     */   public abstract long cachedFreeBusyStartTime(String paramString);
/*     */   
/*     */   public abstract long cachedFreeBusyEndTime();
/*     */   
/*     */   public abstract long cachedFreeBusyEndTime(String paramString);
/*     */   
/*     */   public abstract String foreignPrincipalPrefix();
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/fb/FreeBusyProvider.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */